import { Asset, Prefab, AudioClip, SpriteFrame, Size } from "cc"
import { TResoucesUrl, _ui, _config_, DataLogicHelper, Vector2, Rectangle, Sets, _gameType, TVectorSet, _logic, Maps, Maths, _guide, _scene, winCenterPostion, _language, _timer, _platform } from "../../Main"
import { CSceneData, ILogicData, ELogicDataState, ILogicNuoData, CCubeSize, CNuoDirV2, ILogicNuoMoveSameData } from "../define/LogicDefine"
import { Scene } from "../../../../scene/script/Scene"

const v2T = new Vector2()
const v2T2 = new Vector2()
const v2T3 = new Vector2()
const v2T4 = new Vector2()
const v2T5 = new Vector2()

export class NuoLogic {

    public data: DataLogicHelper<ILogicNuoData> = null!
    public preLoadRes: TResoucesUrl<Asset>[] = []
    public _resetConfigDatas: ILogicNuoData[] = []
    public _resetConfigs: number[] = []

    private _getMoveDatas: {
        datas: ILogicNuoData[],
        remove: ILogicNuoData[],
    } = { datas: [], remove: [] }

    private get scene() { return _scene.getCurrent<Scene>() }

    constructor() {
        this.preLoadRes.push(
            { type: Prefab, url: _ui.DefaultUrl.moveUp },
            { type: Prefab, url: CSceneData.cubeNuoPrefabUrl },
            { type: Prefab, url: CSceneData.tipPrefabUrl },
            { type: Prefab, url: CSceneData.cubeAnimPrefabUrl },
            { type: Prefab, url: CSceneData.selectionPrefabUrl },
            { type: Prefab, url: CSceneData.removeParticle },
            { type: AudioClip, url: "sceneRes/audio/remove" },
        )
        for (let config of _config_.arr.card_item)
            this.preLoadRes.push({ type: SpriteFrame, url: config.icon_url })

        this.data = new DataLogicHelper<ILogicNuoData>(
            () => ({
                dataIndex: -1,
                configId: -1,
                entity: null!,
                isRemove: false,
                index: new Vector2(),
                rect: new Rectangle(),
            }),
            (data) => {
                data.dataIndex = -1
                data.configId = -1
                data.entity = null!
                data.isRemove = false
            }
        )

    }


    public create(
        size: TVectorSet,
    ) {
        if (_platform.isLongScreen)
            _logic.offsetY = 85
        else
            _logic.offsetY = 20

        this.data.reset()
        _logic._create(size)

        let indexs = this.getCardIndexs(_logic.size.x * _logic.size.y / 2)

        for (let x = 0; x < _logic.size.x; x++)
            for (let y = 0; y < _logic.size.y; y++) {
                let data = this.data.create()
                data.dataIndex = _logic.getPosByIndex(x, y, _logic.size)
                data.index.set(x, y)
                this.getIndexByAabbCenter(data)
                data.configId = _config_.arr.card_item[indexs[data.dataIndex]].id
            }


        let datas = this.getStaticSame()
        if (!Maps.shiftKey(datas)) {
            // 保证至少有一对相邻 
            // 交换configIndex
            // 不是最后排 随机一个下面
            let randomX = Maths.zeroToMax(_logic.size.x)
            let randomZ = Maths.zeroToMax(_logic.size.y - 1)
            let index = _logic.getPosByIndex(randomX, randomZ, _logic.size)
            let data = this.data.datas[index]

            let changeIndex = _logic.getPosByIndex(data.index.x, data.index.y + 1, _logic.size)
            let changData = this.data.datas[changeIndex]
            for (let v of this.data.datas) {
                if (v.dataIndex != index) {
                    let configId = v.configId
                    if (configId == data.configId) {
                        v.configId = changData.configId
                        changData.configId = configId
                        break
                    }
                }
            }
        }

    }


    public getMoveDatas(cur: ILogicNuoData, dir: Vector2) {
        this._getMoveDatas.datas.length = 0
        this._getMoveDatas.datas.push(cur)
        let count = 1

        this._getMoveDatas.remove.length = 0

        while (true) {
            v2T.x = cur.index.x + dir.x * count
            v2T.y = cur.index.y + dir.y * count

            let index = _logic.getPosByIndex(v2T.x, v2T.y, _logic.size)
            if (index == -1)
                break

            let data = this.data.datas[index]
            // 已被消除
            if (data.isRemove)
                this._getMoveDatas.remove.push(data)
            else {
                if (this._getMoveDatas.remove.length > 0)
                    break
                this._getMoveDatas.datas.push(data)
            }


            count++
        }
        let max = dir.x !== 0 ? _logic.size.x : _logic.size.y

        // 全部都在
        if (this._getMoveDatas.remove.length == max)
            this._getMoveDatas.remove.length = 0
        return this._getMoveDatas
    }

    public getIndexByAabbCenter(data: ILogicNuoData) {
        _logic.getIndexByAabbCenter(data.rect, data.index, _logic.cubeSize, _logic.offset)
    }

    private getSames() {
        let sames: { [configId: number]: number[] } = {}
        // 所有相同的
        for (let data of this.data.datas) {
            if (!data.isRemove) {
                if (!sames[data.configId])
                    sames[data.configId] = []
                sames[data.configId].push(data.dataIndex)
            }
        }
        return sames
    }

    public propTip() {
        this.scene.clearTips()

        let moveSames = this.getMoveSame()
        if (moveSames) {
            for (let v of moveSames.datas) {
                for (let _v of v)
                    this.data.datas[_v].entity.tipAnim()

                _guide.finger({
                    position: this.data.datas[v[0]].entity.node.worldPosition,
                })
            }

            let dataMoves = moveSames.dataMoves
            if (dataMoves) {

                let index = dataMoves.indexs[0]

                Vector2.add(v2T, this.data.datas[index].rect.center, winCenterPostion())

                v2T2.x = v2T.x + _logic.cubeSize.width * dataMoves.moveCount * dataMoves.dir[0]
                v2T2.y = v2T.y + _logic.cubeSize.height * dataMoves.moveCount * dataMoves.dir[1]

                _guide.moveFinger(
                    v2T,
                    v2T2,
                )
            }
        }
    }

    /**动态可消除的 */
    public getMoveSame() {
        let datas: number[][] = []
        let allDatas: number[] = []

        let sames = this.getSames()
        let statics = this.getStaticSame(sames)
        for (let v in statics) {
            let indexs = statics[v]
            datas.push([indexs[0], indexs[1]])
            Sets.add(allDatas, indexs[0], indexs[1])
        }


        let dataMoves: ILogicNuoMoveSameData = null!
        for (let key in sames) {
            let indexs = sames[key]
            dataMoves = this._getMoveSame(indexs, allDatas)
            if (dataMoves)
                break
        }

        if (datas.length == 0 && !dataMoves)
            return null!

        return { datas, dataMoves }
    }

    public resetConfig() {
        this._resetConfigs.length = 0
        this._resetConfigDatas.length = 0
        let datas = this.data.datas
        for (let v of datas)
            if (!v.isRemove) {
                this._resetConfigDatas.push(v)
                this._resetConfigs.push(v.configId)
                v.entity.playResetAnim()
            }


        _logic.palyResetAnim(() => {

            while (true) {
                Sets.shuffle(this._resetConfigs, true, () => _gameType.curRandom.run())
                for (let i = 0; i < this._resetConfigs.length; i++)
                    this._resetConfigDatas[i].configId = this._resetConfigs[i]

                if (this.getMoveSame())
                    break
            }

            for (let v of this._resetConfigDatas)
                v.entity.stopResetAnim()

            this._resetConfigs.length = 0
            this._resetConfigDatas.length = 0

        })

    }

    public propRemove(count = 2) {

        let datas = this.getSames()
        let arrs: number[][] = []
        for (let key in datas)
            arrs.push(datas[key])
        Sets.shuffle(arrs, true, () => _gameType.curRandom.run())

        let max = Math.min(count, arrs.length)
        for (let i = 0; i < max; i++) {
            let arr = arrs[i]
            this.scene.nuo.remove(this.data.datas[arr[0]], this.data.datas[arr[1]])
        }

        this.checkNoSame()

        _ui.tip("消除{0}对成功".replace("{0}", count + ""))

        _gameType.callLaterCheckComplete()
    }

    public count() {
        let count = 0
        for (let data of this.data.datas)
            if (!data.isRemove)
                count++
        return count
    }

    public checkNoSame() {
        if (this.count() > 2)
            _timer.callLater(this, this._checkNoSame)
    }

    private _checkNoSame() {
        if (this.count() > 2)
            if (_gameType.isRun)
                if (!this.getMoveSame()) {
                    _ui.blockTouchEvent(true)
                    _ui.tip("无牌可消除，即将自动洗牌", false, () => {
                        _ui.blockTouchEvent(false)
                        this.resetConfig()
                    })
                }
    }

    private _getMoveSame(indexs: number[], allDatas: number[]) {
        for (let i of indexs) {
            if (Sets.has(allDatas, i))
                continue
            let data = this.data.datas[i]
            for (let dir of CNuoDirV2) {
                let getMoveDatas = this.getMoveDatas(data, dir)
                let removeCount = getMoveDatas.remove.length
                if (removeCount > 0)
                    for (let _i of indexs) {
                        if (i == _i)
                            continue
                        if (Sets.has(allDatas, _i))
                            continue
                        let _data = this.data.datas[_i]
                        let sub = Vector2.sub(v2T5, _data.index, data.index)

                        for (let _c = 1; _c <= removeCount; _c++) {

                            v2T.x = dir.x * _c
                            v2T.y = dir.y * _c

                            // 可匹配                            
                            if (sub.x == v2T.x || sub.y == v2T.y) {
                                // 中间无阻挡的
                                if (sub.x == v2T.x)
                                    v2T4.set(_data.index.x, data.index.y)
                                else
                                    v2T4.set(data.index.x, _data.index.y)
                                v2T3.set(_data.index.x, _data.index.y)

                                let poss = this.getSameByCenter(v2T4, v2T3)
                                if (poss)
                                    if (!this.hasSameCenter(poss)) {
                                        Sets.add(allDatas, i, _i)
                                        return <ILogicNuoMoveSameData>{
                                            indexs: [i, _i],
                                            dir: [dir.x, dir.y],
                                            removeCount,
                                            moveCount: _c,
                                        }
                                    }
                            }
                        }

                    }
            }
        }
        return null!
    }

    public moveChangeSame(dir: Vector2, datas: ILogicNuoData[], space: number) {
        if (space == 0)
            return null!
        let orginIndexs: Vector2[] = []
        let toIndexs: Vector2[] = []

        for (let data of datas) {
            // 原位置
            let orginPos = Vector2.pool.get().set(data.index)
            orginIndexs.push(orginPos)

            // 移动到的位置
            let to = Vector2.pool.get()
            to.x = data.index.x + dir.x * space
            to.y = data.index.y + dir.y * space
            toIndexs.push(to)
        }

        for (let i = datas.length - 1; i >= 0; i--)
            this._moveChangeSame(orginIndexs[i], toIndexs[i])
        let remove = this.getStaticSameRemove(datas[0])
        if (!remove) {
            // 还原
            for (let i = 0; i < datas.length; i++)
                this._moveChangeSame(toIndexs[i], orginIndexs[i])

            for (let v of orginIndexs)
                Vector2.pool.put(v)
            for (let v of toIndexs)
                Vector2.pool.put(v)
            orginIndexs.length = 0
            toIndexs.length = 0

            remove = null!
        }
        return remove
    }

    public getStaticSameRemove(data: ILogicNuoData) {
        let datas = this.getStaticSame()
        for (let key in datas) {
            let indexs = datas[key]
            for (let v of indexs)
                if (v == data.dataIndex)
                    return indexs
        }

        return null!
    }

    private _moveChangeSame(orginPos: Vector2, toPos: Vector2) {
        let orginIndex = _logic.getPosByIndex(orginPos.x, orginPos.y, _logic.size)
        let orginData = this.data.datas[orginIndex]

        let toIndex = _logic.getPosByIndex(toPos.x, toPos.y, _logic.size)
        let toData = this.data.datas[toIndex]

        if (orginData) {
            orginData.index.set(toPos)
            orginData.dataIndex = toIndex
            this.getIndexByAabbCenter(orginData)
        }
        this.data.datas[orginIndex] = toData


        if (toData) {
            toData.index.set(orginPos)
            toData.dataIndex = orginIndex
            this.getIndexByAabbCenter(toData)
        }
        this.data.datas[toIndex] = orginData

    }

    /**获取静态可消除的 */
    public getStaticSame(sames?: { [configId: number]: number[] }) {

        let datas: { [configId: number]: number[] } = {}
        let allDatas: number[] = []
        if (!sames)
            sames = this.getSames()
        for (let configId in sames) {
            let indexs = sames[configId]

            for (let i of indexs) {
                if (Sets.has(allDatas, i))
                    continue
                let data = this.data.datas[i]
                for (let _i of indexs) {
                    if (i == _i)
                        continue
                    if (Sets.has(allDatas, _i))
                        continue
                    let _data = this.data.datas[_i]
                    let poss = this.getSameByCenter(
                        v2T4.set(data.index.x, data.index.y),
                        v2T3.set(_data.index.x, _data.index.y),
                    )
                    if (poss)
                        if (!this.hasSameCenter(poss)) {
                            if (datas[configId] === undefined)
                                datas[configId] = []
                            Sets.add(datas[configId], i)
                            Sets.add(datas[configId], _i)

                            Sets.add(allDatas, i)
                            Sets.add(allDatas, _i)
                        }
                }
            }
        }
        return datas
    }

    /**中间有 */
    public hasSameCenter(poss: Vector2[]) {
        for (let pos of poss) {
            let index = _logic.getPosByIndex(pos.x, pos.y, _logic.size)
            if (index != -1)
                if (!this.data.datas[index].isRemove)
                    return true
        }
        return false
    }

    public getSameByCenter(start: Vector2, end: Vector2) {
        Vector2.sub(v2T, end, start)
        if (v2T.x == 0 && v2T.y == 0)
            return null!
        if (v2T.x !== 0 && v2T.y !== 0)
            return null!
        Vector2.normalize(v2T2, v2T)
        let len = v2T.len()
        let poss: Vector2[] = []
        for (let i = 1; i < len; i++) {
            let pos = new Vector2()
            pos.x = start.x + v2T2.x * i
            pos.y = start.y + v2T2.y * i
            poss.push(pos)
        }
        return poss
    }

    public getCardIndexs(count: number): number[] {
        let configByIndexs: number[] = []

        let len = _config_.arr.card_item.length
        // 顺序选所有牌
        let mul = Math.floor(count / len)
        for (let i = 0; i < mul; i++) {
            for (let j = 0; j < len; j++) {
                configByIndexs.push(j)
                configByIndexs.push(j)
            }
        }

        // 剩下随机取
        let sub = count - mul * len
        let arr = _config_.arr.card_item.map((v, i) => i)
        Sets.shuffle(arr, true, () => _gameType.curRandom.run())
        for (let i = 0; i < sub; i++) {
            configByIndexs.push(arr[i])
            configByIndexs.push(arr[i])
        }

        Sets.shuffle(configByIndexs, true, () => _gameType.curRandom.run())
        return configByIndexs
    }


}